﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace AnthStat.Infrastructure
{
    public abstract class XmlRepositoryBase<T> : IRepository<T>
    {
        #region Properties
        public virtual XElement ParentElement { get; protected set; }
        protected XName ElementName { get; private set; }
        protected abstract Func<XElement, T> Selector { get; }
        protected XDocument Document { get; set; }
        private string FileName { get; set; }
        #endregion // Properties

        #region Constructors
        protected XmlRepositoryBase(string fileName, XName elementName)
        {
            ElementName = elementName;
            FileName = fileName;
            Document = XDocument.Load(fileName);
            // clears the "cached" ParentElement to allow hot file changes
            //XDocumentProvider.Default.CurrentDocumentChanged += (sender, eventArgs) => ParentElement = null;
        }
        #endregion // Constructors

        #region Methods

        protected abstract void SetXElementValue(T model, XElement element);

        protected abstract XElement CreateXElement(T model);

        protected abstract object GetEntityId(T entity);

        #region IRepository<T>

        public T GetByKey(object keyValue)
        {
            return Document.Descendants(ElementName)
                .Where(e => e.Attribute("Id").Value == keyValue.ToString())
                .Select(Selector)
                .FirstOrDefault();
        }

        public IEnumerable<T> GetAll()
        {
            return ParentElement.Elements(ElementName).Select(Selector);
        }

        public virtual IEnumerable<T> GetAll(object parentId)
        {
            throw new InvalidOperationException("This entity doesn't contains a parent.");
        }

        public virtual void Insert(T entity, bool autoPersist = true)
        {
            ParentElement.Add(CreateXElement(entity));

            if (autoPersist)
                Save();
        }

        public virtual void Update(T entity, bool autoPersist= true)
        {
            // I intend to remove this magic string "Id"
            SetXElementValue(
                entity,
                ParentElement.Elements().FirstOrDefault(a => a.Attribute("Id").Value == GetEntityId(entity).ToString()
            ));

            if (autoPersist)
                Save();
        }

        public virtual void Delete(T entity, bool autoPersist = true)
        {
            ParentElement.Elements().FirstOrDefault(a => a.Attribute("Id").Value == GetEntityId(entity).ToString()).Remove();

            if (autoPersist)
                Save();
        }

        public virtual string Save()
        {
            StringBuilder sb = new StringBuilder();
            XmlWriterSettings xws = new XmlWriterSettings();
            xws.OmitXmlDeclaration = false;
            xws.Indent = true;

            using (XmlWriter xw = XmlWriter.Create(sb, xws))
            {
                Document.Save(xw);
            }

            return sb.ToString(); // hack, because there is no common way to save files among desktop, phone, and WinRT apps, so we have to handle this in the viewmodels. Ugly, but it works...
        }
        #endregion

        #endregion // Methods
    }
}
